/**
 * Metaphase Editor - WYSIWYG HTML Editor Component
 * Copyright (C) 2010  Rudolf Visagie
 * Full text of license can be found in com/metaphaseeditor/LICENSE.txt
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * The author can be contacted at metaphase.editor@gmail.com.
 */

package com.metaphaseeditor;

import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.html.HTMLDocument;

/**
 *
 * @author Rudolf Visagie
 */
public class FindReplaceDialog extends javax.swing.JDialog {

    private JTextPane htmlTextPane;
    private int lastMatchPos = -1;
    private boolean replacePerformed = false;
    
    /** Creates new form FindReplaceDialog */
    public FindReplaceDialog(java.awt.Frame parent, boolean modal, JTextPane htmlTextPane) {
        super(parent, modal);
        initComponents();
        setIconImage(new javax.swing.ImageIcon(getClass().getResource("/com/metaphaseeditor/icons/metaphase16x16.png")).getImage());
        this.htmlTextPane = htmlTextPane;

        setLocationRelativeTo(null);

        htmlTextPane.select(0, 0);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        findWhatLabel = new javax.swing.JLabel();
        findWhatTextField = new javax.swing.JTextField();
        replaceWithLabel = new javax.swing.JLabel();
        replaceWithTextField = new javax.swing.JTextField();
        matchCaseCheckBox = new javax.swing.JCheckBox();
        closeButton = new javax.swing.JButton();
        replaceAllButton = new javax.swing.JButton();
        replaceButton = new javax.swing.JButton();
        findButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Find/Replace");

        findWhatLabel.setText("Find What");

        findWhatTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyTyped(java.awt.event.KeyEvent evt) {
                findWhatTextFieldKeyTyped(evt);
            }
        });

        replaceWithLabel.setText("Replace With");

        matchCaseCheckBox.setText("Match Case");

        closeButton.setText("Close");
        closeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeButtonActionPerformed(evt);
            }
        });

        replaceAllButton.setText("Replace All");
        replaceAllButton.setEnabled(false);
        replaceAllButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                replaceAllButtonActionPerformed(evt);
            }
        });

        replaceButton.setText("Replace");
        replaceButton.setEnabled(false);
        replaceButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                replaceButtonActionPerformed(evt);
            }
        });

        findButton.setText("Find");
        findButton.setEnabled(false);
        findButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                findButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(matchCaseCheckBox)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(replaceWithLabel)
                            .addComponent(findWhatLabel))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(findWhatTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE)
                            .addComponent(replaceWithTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 313, Short.MAX_VALUE)))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(findButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(replaceButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(replaceAllButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(closeButton)))
                .addContainerGap())
        );

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {closeButton, findButton, replaceAllButton, replaceButton});

        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(findWhatLabel)
                    .addComponent(findWhatTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(replaceWithLabel)
                    .addComponent(replaceWithTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(matchCaseCheckBox)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(closeButton)
                    .addComponent(replaceAllButton)
                    .addComponent(replaceButton)
                    .addComponent(findButton))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed
        setVisible(false);
    }//GEN-LAST:event_closeButtonActionPerformed

    private boolean findNext() {
        try {
            boolean matchCase = matchCaseCheckBox.isSelected();
            String findWhat = findWhatTextField.getText();
            HTMLDocument htmlDocument = (HTMLDocument)htmlTextPane.getDocument();
            String htmlText = htmlDocument.getText(0, htmlDocument.getLength());
            if (matchCase) {
                lastMatchPos = htmlText.indexOf(findWhat, lastMatchPos+1);
            } else {
                lastMatchPos = htmlText.toUpperCase().indexOf(findWhat.toUpperCase(), lastMatchPos+1);
            }

            if (lastMatchPos != -1) {
                htmlTextPane.setCaretPosition(lastMatchPos + findWhat.length());
                htmlTextPane.requestFocus();
                htmlTextPane.select(lastMatchPos, lastMatchPos + findWhat.length());
            }

            return lastMatchPos != -1;
        } catch (BadLocationException e) {
            throw new MetaphaseEditorException(e.getMessage(), e);
        }
    }

    private void findButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_findButtonActionPerformed
        if (!findNext()) {
            JOptionPane.showMessageDialog(this, "The specified search text could not be found.");
        }
    }//GEN-LAST:event_findButtonActionPerformed

    private void findWhatTextFieldKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_findWhatTextFieldKeyTyped
        if (findWhatTextField.getText().length() > 0) {
            findButton.setEnabled(true);
            replaceButton.setEnabled(true);
            replaceAllButton.setEnabled(true);
        } else {
            findButton.setEnabled(false);
            replaceButton.setEnabled(false);
            replaceAllButton.setEnabled(false);
        }
    }//GEN-LAST:event_findWhatTextFieldKeyTyped

    private void replaceSelection() {
        try {
            HTMLDocument htmlDocument = (HTMLDocument)htmlTextPane.getDocument();
            String findWhat = findWhatTextField.getText();
            String replaceWith = replaceWithTextField.getText();
            AttributeSet attributeSet = null;
            Element element = htmlDocument.getCharacterElement(lastMatchPos);
            attributeSet = element.getAttributes();
            htmlDocument.remove(lastMatchPos, findWhat.length());
            htmlDocument.insertString(lastMatchPos, replaceWith, attributeSet);
            htmlTextPane.setCaretPosition(lastMatchPos + replaceWith.length());
            htmlTextPane.requestFocus();
            htmlTextPane.select(lastMatchPos, lastMatchPos + replaceWith.length());
        } catch (BadLocationException e) {
            throw new MetaphaseEditorException(e.getMessage(), e);
        }
    }

    private void replaceButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_replaceButtonActionPerformed
        String selectedText = htmlTextPane.getSelectedText();
        if (replacePerformed || selectedText == null || selectedText.length() == 0) {
            replacePerformed = false;
            findButtonActionPerformed(evt);
            return;
        } else if (lastMatchPos > -1) {
            replaceSelection();
            replacePerformed = true;
        } else {
            JOptionPane.showMessageDialog(this, "The specified search text could not be found.");
        }
    }//GEN-LAST:event_replaceButtonActionPerformed

    private void replaceAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_replaceAllButtonActionPerformed
        int count = 0;
        while (findNext()) {
            replaceSelection();
            count++;
        }
        JOptionPane.showMessageDialog(this, count + " replacements have been made.");
    }//GEN-LAST:event_replaceAllButtonActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton closeButton;
    private javax.swing.JButton findButton;
    private javax.swing.JLabel findWhatLabel;
    private javax.swing.JTextField findWhatTextField;
    private javax.swing.JCheckBox matchCaseCheckBox;
    private javax.swing.JButton replaceAllButton;
    private javax.swing.JButton replaceButton;
    private javax.swing.JLabel replaceWithLabel;
    private javax.swing.JTextField replaceWithTextField;
    // End of variables declaration//GEN-END:variables

}
